﻿#region Legal

// Jimmy Zimmerman
// Team Mongoose
//
// END USER LICENSE AGREEMENT
// IMPORTANT - READ THIS CAREFULLY:  This End User License Agreement is a legal agreement
// between you (either an individual, business entity, currently active identity of an
// individual with Multiple Personality Disorder, octopus overlord, or otherworldly entity),
// and Team Mongoose, for the enclosed, components.
//
// By reading this document and/or installing this product, you agree to be bound by the
// terms of this EULA.
//
// Team Mongoose owns all copyright, trade secret, trademark, trade wars,
// patent, portent, and potential rights to these components.  Team Mongoose
// grants you the right to deploy the enclosed components.
//
// If you agree to the terms of this EULA, a license to use these components is granted to you.
//
// If you should happen to benefit from the enclosed components, then you are legally and
// contractually bound to thank us for it. Send your regards to jimmyzimms@hotmail.com.
//
// OTHER RIGHTS AND LIMITATONS
// You may not reverse-engineer, decompile, decode, disassemble, psycho-analyze, or otherwise
// attempt to find hidden meanings between the lines of this EULA; unless, of course, you should
// happen to find some, and they are funny.
//
// You may not translate this EULA or any part of the components into Ancient Sumerian.
//
// THESE COMPONENTS ARE PROVIDED “AS-IS” WITHOUT WARRANTY OF ANY KIND. ANY USE OF THE COMPONENTS
// CONTAINED IS AT YOUR OWN RISK. TEAM MONGOOSE DISCLAIM ALL WARRANTIES, EITHER
// EXPRESS OR IMPLIED, WITH RESPECT TO THE ACCURRACY AND CORRECTNESS OF THE COMPONENTS CONTAINED
// HEREIN. TEAM MONGOOSE DOES NOT WARRANT THAT THE COMPONENTS ARE FLAWLESS.
//
// REDISTRIBUTION AND USE IN SOURCE AND BINARY FORMS, WITH OR WITHOUT MODIFICATION, ARE PERMITTED
// PROVIDED THAT THE FOLLOWING CONDITIONS ARE MET:
// * REDISTRIBUTIONS OF SOURCE CODE MUST RETAIN THE ABOVE COPYRIGHT NOTICE
// * REDISTRIBUTIONS IN BINARY FORM MUST NOTE THE USE OF THE COMPONENT IN DOCUMENTATION AND/OR
//   OTHER MATERIALS PROVIDED WITH THE DISTRIBUTION.
// * NEITHER THE NAME OF TEAM MONGOOSE MAY BE USED TO ENDORES OR PROMOTE PRODUCTS
//   DERIVED FROM THIS SOFTWARE WITHOUT SPECIFIC PRIOR WRITTEN PERMISSION.
//
// IN NO EVENT SHALL TEAM MONGOOSE BE HELD LIABLE FOR INCIDENTAL, SPECIAL, INDIRECT,
// INCONSEQUENTIAL, UNBELIEVABLE, EXAGGERATED, VERBOSE, OR TYPICAL DAMAGES INCURRED WHILE USING
// THE ENCLOSED COMPONENTS.
//
// OUR STUFF ALWAYS WORKS - SOMETIMES.

#endregion

using Microsoft.AnalysisServices.AdomdClient;
using System.Collections;
using System.Collections.Generic;
using System.Data.Common;

namespace System.Data.MdxClient
{
    /// <summary>
    /// Represents a MDX data reader.
    /// </summary>
    public class MdxDataReader : DbDataReader, IEnumerable<IDataRecord>
    {
        #region Fields

        private readonly IDbConnection connection;
        private readonly AdomdDataReader reader;
        private readonly CommandBehavior behavior;
        private bool hasRows;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the internal, native ADOMD.NET data reader.
        /// </summary>
        /// <value>An <see cref="AdomdDataReader"/> object.</value>
        protected internal AdomdDataReader AdomdDataReader
        {
            get
            {
                return this.reader;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the object has been disposed.
        /// </summary>
        /// <value>True if the object has been disposed; otherwise, false.s</value>
        protected bool IsDisposed
        {
            get;
            private set;
        }

        #endregion

        #region Overrides

        /// <summary>
        /// Closes the reader.
        /// </summary>
        public override void Close()
        {
            this.reader.Close();

            if ( this.behavior == CommandBehavior.CloseConnection )
                this.connection.Close();
        }

        /// <summary>
        /// Gets the depth of nesting in the current row.
        /// </summary>
        /// <value>The depth of nesting in the current row.</value>
        public override int Depth
        {
            get
            {
                return this.reader.Depth;
            }
        }

        /// <summary>
        /// Releases the managed and, optionally, the unmanaged resources used by the <see cref="MdxDataReader"/> class.
        /// </summary>
        /// <param name="disposing">Indicates whether the object is being disposed.</param>
        protected override void Dispose( bool disposing )
        {
            if ( this.IsDisposed )
                return;

            this.IsDisposed = true;
            this.Close();
            this.reader.Dispose();
            base.Dispose( disposing );
        }

        /// <summary>
        /// Gets the number of fields in the current row.
        /// </summary>
        /// <value>The number of fields in the current row.</value>
        public override int FieldCount
        {
            get
            {
                return this.reader.FieldCount;
            }
        }

        /// <summary>
        /// Returns the Boolean value for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>A <see cref="Boolean"/> structure.</returns>
        public override bool GetBoolean( int ordinal )
        {
            return this.reader.GetBoolean( ordinal );
        }

        /// <summary>
        /// Returns the unsigned byte value for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>A <see cref="Byte"/> structure.</returns>
        public override byte GetByte( int ordinal )
        {
            return this.reader.GetByte( ordinal );
        }

        /// <summary>
        /// Fills the specified buffer with bytes from the specified column and returns the number of bytes written.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <param name="dataOffset">The offset in the data where reading begins.</param>
        /// <param name="buffer">The buffer to copy the bytes to.</param>
        /// <param name="bufferOffset">The offset in the buffer where writting begins.</param>
        /// <param name="length">The requested number of bytes to read and write.</param>
        /// <returns>The actual number of bytes read and written.</returns>
        public override long GetBytes( int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length )
        {
            return this.reader.GetBytes( ordinal, dataOffset, buffer, bufferOffset, length );
        }

        /// <summary>
        /// Returns the character for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>A <see cref="Char"/> structure.</returns>
        public override char GetChar( int ordinal )
        {
            return this.reader.GetChar( ordinal );
        }

        /// <summary>
        /// Fills the specified buffer with characters from the specified column and returns the number characters written.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <param name="dataOffset">The offset in the data where reading begins.</param>
        /// <param name="buffer">The buffer to copy the characters to.</param>
        /// <param name="bufferOffset">The offset in the buffer where writting begins.</param>
        /// <param name="length">The requested number of characters to read and write.</param>
        /// <returns>The actual number of characters read and written.</returns>
        public override long GetChars( int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length )
        {
            return this.reader.GetChars( ordinal, dataOffset, buffer, bufferOffset, length );
        }

        /// <summary>
        /// Returns the name of the data type for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>The name of the column data type.</returns>
        public override string GetDataTypeName( int ordinal )
        {
            return this.reader.GetDataTypeName( ordinal );
        }

        /// <summary>
        /// Returns the date and time for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>A <see cref="DateTime"/> structure.</returns>
        public override DateTime GetDateTime( int ordinal )
        {
            return this.reader.GetDateTime( ordinal );
        }

        /// <summary>
        /// Returns a data reader for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>A <see cref="MdxDataReader"/> object.</returns>
        protected override DbDataReader GetDbDataReader( int ordinal )
        {
            return new MdxDataReader( this.connection, this.reader.GetDataReader( ordinal ), this.behavior );
        }

        /// <summary>
        /// Returns the decimal value for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>A <see cref="Decimal"/> structure.</returns>
        public override decimal GetDecimal( int ordinal )
        {
            return this.reader.GetDecimal( ordinal );
        }

        /// <summary>
        /// Returns the double floating-point precision value for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>A <see cref="Double"/> structure.</returns>
        public override double GetDouble( int ordinal )
        {
            return this.GetDouble( ordinal );
        }

        /// <summary>
        /// Returns an enumerator for the reader.
        /// </summary>
        /// <returns>An <see cref="IEnumerator"/> object.</returns>
        public override IEnumerator GetEnumerator()
        {
            return this.reader.GetEnumerator();
        }

        /// <summary>
        /// Returns the field type for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>A <see cref="Type"/> object.</returns>
        public override Type GetFieldType( int ordinal )
        {
            return this.reader.GetFieldType( ordinal );
        }

        /// <summary>
        /// Returns the single, floating-point precision value for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>A <see cref="Single"/> structure.</returns>
        public override float GetFloat( int ordinal )
        {
            return this.reader.GetFloat( ordinal );
        }

        /// <summary>
        /// Returns the Globally Unique Identifier (GUID) for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>A <see cref="Guid"/> structure.</returns>
        public override Guid GetGuid( int ordinal )
        {
            return this.reader.GetGuid( ordinal );
        }

        /// <summary>
        /// Returns the signed, 16-bit integer for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>An <see cref="Int16"/> structure.</returns>
        public override short GetInt16( int ordinal )
        {
            return this.reader.GetInt16( ordinal );
        }

        /// <summary>
        /// Returns the signed, 32-bit integer for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>An <see cref="Int32"/> structure.</returns>
        public override int GetInt32( int ordinal )
        {
            return this.reader.GetInt32( ordinal );
        }

        /// <summary>
        /// Returns the signed, 64-bit integer for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>An <see cref="Int64"/> structure.</returns>
        public override long GetInt64( int ordinal )
        {
            return this.reader.GetInt64( ordinal );
        }

        /// <summary>
        /// Returns the name of the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>The name of the specified column.</returns>
        public override string GetName( int ordinal )
        {
            return this.reader.GetName( ordinal );
        }

        /// <summary>
        /// Returns the oridinal position of the specified column name.
        /// </summary>
        /// <param name="name">The name of the column.</param>
        /// <returns>The zero-based index of the column in the current row.</returns>
        public override int GetOrdinal( string name )
        {
            return this.reader.GetOrdinal( name );
        }

        /// <summary>
        /// Returns a schema table for the reader.
        /// </summary>
        /// <returns>A <see cref="DataTable"/> object.</returns>
        public override DataTable GetSchemaTable()
        {
            return this.reader.GetSchemaTable();
        }

        /// <summary>
        /// Returns a string for the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>A <see cref="String"/> object.</returns>
        public override string GetString( int ordinal )
        {
            return this.reader.GetString( ordinal );
        }

        /// <summary>
        /// Returns the value of the specified column.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>An <see cref="Object"/> representing the column value.</returns>
        public override object GetValue( int ordinal )
        {
            return this.reader.GetValue( ordinal );
        }

        /// <summary>
        /// Fills the specified array with all of the values in the current row.
        /// </summary>
        /// <param name="values">The array to write the column values into.</param>
        /// <returns>The number of values written.</returns>
        public override int GetValues( object[] values )
        {
            return this.reader.GetValues( values );
        }

        /// <summary>
        /// Gets a value indicating whether the reader has any rows.
        /// </summary>
        /// <value>True if the data reader has any rows; otherwise, false.</value>
        /// <exception cref="InvalidOperationException">The reader is closed.</exception>
        public override bool HasRows
        {
            get
            {
                if ( this.IsClosed )
                    throw new InvalidOperationException( SR.InvalidOpReaderClosed );

                return this.hasRows;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the reader is closed.
        /// </summary>
        /// <value>True if the data reader is closed; otherwise, false.</value>
        public override bool IsClosed
        {
            get
            {
                return this.reader.IsClosed;
            }
        }

        /// <summary>
        /// Returns a value indicating whether the specified column value represents <see cref="DBNull"/>.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>True if the specified column value represents <see cref="DBNull"/>; otherwise, false.</returns>
        public override bool IsDBNull( int ordinal )
        {
            return this.reader.IsDBNull( ordinal );
        }

        /// <summary>
        /// Advances the data reader to the next result set.
        /// </summary>
        /// <returns>True if the data reader advanced to the next result set; otherwise, false.</returns>
        public override bool NextResult()
        {
            return this.reader.NextResult();
        }

        /// <summary>
        /// Advances the reader to the next row.
        /// </summary>
        /// <returns>True if the data reader advanced to the next row; otherwise, false.</returns>
        public override bool Read()
        {
            this.hasRows = this.reader.Read();
            return this.hasRows;
        }

        /// <summary>
        /// Gets the number of rows affected by the reader.
        /// </summary>
        /// <value>The total number of rows affected.</value>
        public override int RecordsAffected
        {
            get
            {
                return this.reader.RecordsAffected;
            }
        }

        /// <summary>
        /// Gets the value of the column at the specified index.
        /// </summary>
        /// <param name="ordinal">The zero-based, ordinal position of the column to retrieve.</param>
        /// <returns>An <see cref="Object"/> representing the column value.</returns>
        public override object this[int ordinal]
        {
            get
            {
                return this.reader[ordinal];
            }
        }

        /// <summary>
        /// Gets the value of the column with the specified name.
        /// </summary>
        /// <param name="name">The name of the column to retrieve.</param>
        /// <returns>An <see cref="Object"/> representing the column value.</returns>
        public override object this[string name]
        {
            get
            {
                return this.reader[name];
            }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Instantiates a new instance of the <see cref="MdxDataReader"/> class.
        /// </summary>
        /// <param name="connection">The <see cref="IDbConnection"/> for the data reader.  This could be a
        /// <see cref="MdxConnection"/> or an <see cref="AdomdConnection"/> object.</param>
        /// <param name="reader">The adapted <see cref="AdomdDataReader"/> object.</param>
        protected internal MdxDataReader( IDbConnection connection, AdomdDataReader reader )
            : this( connection, reader, CommandBehavior.Default )
        {
        }

        /// <summary>
        /// Instantiates a new instance of the <see cref="MdxDataReader"/> class.
        /// </summary>
        /// <param name="connection">The <see cref="IDbConnection"/> for the data reader.  This could be a
        /// <see cref="MdxConnection"/> or an <see cref="AdomdConnection"/> object.</param>
        /// <param name="reader">The adapted <see cref="AdomdDataReader"/> object.</param>
        /// <param name="behavior">One of the <see cref="CommandBehavior"/> values.</param>
        protected internal MdxDataReader( IDbConnection connection, AdomdDataReader reader, CommandBehavior behavior )
        {
            if ( connection == null )
                throw new ArgumentNullException( "connection" );
            if ( reader == null )
                throw new ArgumentNullException( "reader" );

            this.connection = connection;
            this.reader = reader;
            this.behavior = behavior;
        }

        #endregion

        #region IEnumerable<IDataRecord> Members

        IEnumerator<IDataRecord> IEnumerable<IDataRecord>.GetEnumerator()
        {
            foreach ( IDataRecord record in ( (IEnumerable) this ) )
                yield return record;
        }

        #endregion
    }
}
